﻿using Microsoft.EntityFrameworkCore;
using System.Data;
using System.Data.Common;
using System.Linq.Expressions;

namespace Aore.Core.Repository.EFCore;

public class EfRepository<T> : IEfRepository<T> where T : class
{
  /// <summary>
  /// 数据库操作类
  /// </summary>
  private readonly DbContext _context;

  /// <summary>
  /// 构造函数
  /// </summary>
  /// <param name="context"></param>
  public EfRepository(ApplicationDbContext context)
  {
    _context = context;
  }
  
  /// <summary>
  /// 根据id查询
  /// </summary>
  /// <param name="id"></param>
  /// <returns></returns>
  public T? GetById(object id)
  {
    return _context.Set<T>().Find(id);
  }

  /// <summary>
  /// 查询所有
  /// </summary>
  public IEnumerable<T> GetAll()
  {
    return _context.Set<T>().AsNoTracking().ToList();
  }

  /// <summary>
  /// 查询
  /// </summary>
  /// <param name="predicate"></param>
  /// <returns></returns>
  public IEnumerable<T> Find(Expression<Func<T, bool>> predicate)
  {
    return _context.Set<T>().Where(predicate).AsNoTracking().ToList();
  }

  /// <summary>
  /// 根据id查询
  /// </summary>
  /// <param name="id"></param>
  /// <returns></returns>
  public async Task<T?> GetByIdAsync(object id)
  {
    return await _context.Set<T>().FindAsync(id);
  }

  /// <summary>
  /// 查询所有
  /// </summary>
  public async Task<IEnumerable<T>> GetAllAsync()
  {
    return await _context.Set<T>().AsNoTracking().ToListAsync();
  }

  /// <summary>
  /// 查询
  /// </summary>
  /// <param name="predicate"></param>
  /// <returns></returns>
  public async Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate)
  {
    return await _context.Set<T>().Where(predicate).AsNoTracking().ToListAsync();
  }
  
  /// <summary>
  /// 添加
  /// </summary>
  /// <param name="entity"></param>
  /// <returns></returns>
  public T Add(T entity)
  {
    _context.Set<T>().Add(entity);
    return entity;
  }

  /// <summary>
  /// 批量添加
  /// </summary>
  /// <param name="entities"></param>
  public void AddRange(IEnumerable<T> entities)
  {
    _context.Set<T>().AddRange(entities);
  }

  /// <summary>
  /// 添加
  /// </summary>
  /// <param name="entity"></param>
  /// <returns></returns>
  public async Task<T> AddAsync(T entity)
  {
    await _context.Set<T>().AddAsync(entity);
    return entity;
  }

  /// <summary>
  /// 批量添加
  /// </summary>
  /// <param name="entities"></param>
  public async Task AddRangeAsync(IEnumerable<T> entities)
  {
    await _context.Set<T>().AddRangeAsync(entities);
  }

  /// <summary>
  /// 修改
  /// </summary>
  /// <param name="entity"></param>
  public void Update(T entity)
  {
    _context.Set<T>().Update(entity);
  }

  /// <summary>
  /// 删除
  /// </summary>
  /// <param name="entity"></param>
  public void Remove(T entity)
  {
    _context.Set<T>().Remove(entity);
  }

  /// <summary>
  /// 批量删除
  /// </summary>
  /// <param name="entities"></param>
  public void RemoveRange(IEnumerable<T> entities)
  {
    _context.Set<T>().RemoveRange(entities);
  }
  
  /// <summary>
  /// 保存
  /// </summary>
  /// <returns></returns>
  public int SaveChanges()
  {
    return _context.SaveChanges();
  }

  /// <summary>
  /// 保存
  /// </summary>
  /// <returns></returns>
  public async Task<int> SaveChangesAsync()
  {
    return await _context.SaveChangesAsync();
  }
  
  /// <summary>
  /// 原生SQL实现
  /// </summary>
  /// <param name="sql"></param>
  /// <param name="parameters"></param>
  /// <returns></returns>
  public IEnumerable<T> FromSqlRaw(string sql, params DbParameter[] parameters)
  {
    return _context.Set<T>().FromSqlRaw(sql, parameters).AsNoTracking().ToList();
  }

  /// <summary>
  /// 原生SQL实现
  /// </summary>
  /// <param name="sql"></param>
  /// <param name="parameters"></param>
  /// <returns></returns>
  public int ExecuteSqlRaw(string sql, params object[] parameters)
  {
    return _context.Database.ExecuteSqlRaw(sql, parameters);
  }

  /// <summary>
  /// 存储过程实现
  /// </summary>
  /// <param name="procName"></param>
  /// <param name="parameters"></param>
  /// <returns></returns>
  public IEnumerable<T> ExecuteStoredProcedure(string procName, params DbParameter[] parameters)
  {
    string paramNames = string.Join(", ", parameters.Select(p => $"@{p.ParameterName}"));
    string sql = $"EXEC {procName} {paramNames}";
    return FromSqlRaw(sql, parameters);
  }

  /// <summary>
  /// 存储过程执行
  /// </summary>
  /// <param name="procName"></param>
  /// <param name="parameters"></param>
  /// <returns></returns>
  public DbDataReader ExecuteStoredProcedureReader(string procName, params DbParameter[] parameters)
  {
    DbConnection connection = _context.Database.GetDbConnection();
    connection.Open();

    using DbCommand command = connection.CreateCommand();
    command.CommandText = procName;
    command.CommandType = CommandType.StoredProcedure;

    foreach (DbParameter param in parameters)
    {
      command.Parameters.Add(param);
    }

    return command.ExecuteReader();
  }

  /// <summary>
  /// 原生SQL实现
  /// </summary>
  /// <param name="sql"></param>
  /// <param name="parameters"></param>
  /// <returns></returns>
  public async Task<IEnumerable<T>> FromSqlRawAsync(string sql, params DbParameter[] parameters)
  {
    return await _context.Set<T>().FromSqlRaw(sql, parameters).AsNoTracking().ToListAsync();
  }

  /// <summary>
  /// 原生SQL实现
  /// </summary>
  /// <param name="sql"></param>
  /// <param name="parameters"></param>
  /// <returns></returns>
  public async Task<int> ExecuteSqlRawAsync(string sql, params object[] parameters)
  {
    return await _context.Database.ExecuteSqlRawAsync(sql, parameters);
  }

  /// <summary>
  /// 存储过程实现
  /// </summary>
  /// <param name="procName"></param>
  /// <param name="parameters"></param>
  /// <returns></returns>
  public async Task<IEnumerable<T>> ExecuteStoredProcedureAsync(string procName, params DbParameter[] parameters)
  {
    string paramNames = string.Join(", ", parameters.Select(p => $"@{p.ParameterName}"));
    string sql = $"EXEC {procName} {paramNames}";
    return await FromSqlRawAsync(sql, parameters);
  }

  /// <summary>
  /// 存储过程执行
  /// </summary>
  /// <param name="procName"></param>
  /// <param name="parameters"></param>
  /// <returns></returns>
  public async Task<DbDataReader> ExecuteStoredProcedureReaderAsync(string procName, params DbParameter[] parameters)
  {
    DbConnection connection = _context.Database.GetDbConnection();
    await connection.OpenAsync();

    using DbCommand command = connection.CreateCommand();
    command.CommandText = procName;
    command.CommandType = CommandType.StoredProcedure;

    foreach (DbParameter param in parameters)
    {
      command.Parameters.Add(param);
    }

    return await command.ExecuteReaderAsync();
  }
}